/* 
 * File:   Utility.h
 * Author: johannes
 *
 * Created on January 9, 2014, 2:49 PM
 */
#ifndef UTILITY_H
#define UTILITY_H

#include <cmath>
#include <stdlib.h>
#include <utility>
#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <SDL2/SDL.h>

namespace GUL {
    namespace System {
        

        typedef std::pair<std::size_t, std::size_t> UPair;
        typedef std::pair<int, int> IPair;
        typedef std::pair<double, double> Pos2DFloat;
        typedef IPair Pos2DInt;
        typedef Pos2DFloat Pos2D;
        
        
        ///
        
        template<typename t> std::string ToString(t num)
        {
            std::stringstream stream;
            stream << num ;
            return stream.str();
        }
        
        


        ///Auxiliary structures-----------------------------------------------------
        //

        struct Circle {
            int x, y;
            int r;
        };

        //Rectangle

        class Rect {
        private:
            SDL_Rect m_Rectangle;
        protected:
        public:
            Rect( const Pos2DInt& p,const IPair& s)
            {
                this->m_Rectangle.h = s.second;
                this->m_Rectangle.w = s.first;
                this->m_Rectangle.x = p.first;
                this->m_Rectangle.y = p.second;
            };

            Rect(int x, int y, int w, int h) {
                this->m_Rectangle.h = h;
                this->m_Rectangle.w = w;
                this->m_Rectangle.x = x;
                this->m_Rectangle.y = y;
            };

            virtual ~Rect() {
            };

            inline operator SDL_Rect*() {
                return &this->m_Rectangle;
            }

            inline Pos2DInt Position() {
                return static_cast<Pos2DInt> (std::make_pair(this->m_Rectangle.x, this->m_Rectangle.y));
            };

            inline int X() {
                return this->m_Rectangle.x;
            };

            inline int Y() {
                return this->m_Rectangle.y;
            };

            inline IPair Size() {
                return static_cast<Pos2DInt> (std::make_pair(this->m_Rectangle.w, this->m_Rectangle.h));
            };

            inline int Width() {
                return this->m_Rectangle.w;
            };

            inline int Height() {
                return this->m_Rectangle.h;
            };

        };

        inline double distance(int x1, int y1, int x2, int y2) {
            //Return the distance between the two points
            return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
        };

        inline bool check_collision_Rect(std::vector<Rect> &A, std::vector<Rect> &B) { //The sides of the rectangles
            std::size_t leftA, leftB;
            std::size_t rightA, rightB;
            std::size_t topA, topB;
            std::size_t bottomA, bottomB;

            //Go through the A boxes
            for (std::size_t Abox = 0; Abox < A.size(); Abox++) {
                //Calculate the sides of rect A
                leftA = A[ Abox ].X();
                rightA = A[ Abox ].X() + A[ Abox ].Width();
                topA = A[ Abox ].Y();
                bottomA = A[ Abox ].Y() + A[ Abox ].Height();

                //Go through the B boxes    
                for (std::size_t Bbox = 0; Bbox < B.size(); Bbox++) {
                    //Calculate the sides of rect B
                    leftB = B[ Bbox ].X();
                    rightB = B[ Bbox ].X() + B[ Bbox ].Width();
                    topB = B[ Bbox ].Y();
                    bottomB = B[ Bbox ].Y() + B[ Bbox ].Height();

                    //If no sides from A are outside of B
                    if (((bottomA <= topB) || (topA >= bottomB) || (rightA <= leftB) || (leftA >= rightB)) == false) {
                        //A collision is detected
                        return true;
                    }
                }
            }

            //If neither set of collision boxes touched
            return false;
        };

        inline bool check_collision_Rect(Rect A, Rect B) {
            //The sides of the rectangles
            std::size_t leftA, leftB;
            std::size_t rightA, rightB;
            std::size_t topA, topB;
            std::size_t bottomA, bottomB;

            //Calculate the sides of rect A
            leftA = A.X();
            rightA = A.X() + A.Width();
            topA = A.Y();
            bottomA = A.Y() + A.Height();

            //Calculate the sides of rect B
            leftB = B.X();
            ;
            rightB = B.X() + B.Width();
            topB = B.Y();
            bottomB = B.Y() + B.Height();
            //If any of the sides from A are outside of B
            if (bottomA <= topB) {
                return false;
            }

            if (topA >= bottomB) {
                return false;
            }

            if (rightA <= leftB) {
                return false;
            }

            if (leftA >= rightB) {
                return false;
            }

            //If none of the sides from A are outside B
            return true;
        };

        inline bool check_collision_Circle(Circle &A, Circle &B) {
            //If the distance between the centers of the circles is less than the sum of their radii
            if (distance(A.x, A.y, B.x, B.y) < (A.r + B.r)) {
                //The circles have collided
                return true;
            }

            //If not
            return false;
        };

        inline bool check_collision_Circle(Circle &A, std::vector<Rect> &B) {
            //Closest point on collision box
            std::size_t cX, cY;

            //Go through the B boxes
            for (std::size_t Bbox = 0; Bbox < B.size(); Bbox++) {

                //Find closest x offset
                if (A.x < B[ Bbox ].X()) {
                    cX = B[ Bbox ].X();
                } else if (A.x > B[ Bbox ].X() + B[ Bbox ].Width()) {
                    cX = B[ Bbox ].X() + B[ Bbox ].Width();
                } else {
                    cX = A.x;
                }

                //Find closest y offset
                if (A.y < B[ Bbox ].Y()) {
                    cY = B[ Bbox ].Y();
                } else if (A.y > B[ Bbox ].Y() + B[ Bbox ].Height()) {
                    cY = B[ Bbox ].Y() + B[ Bbox ].Height();
                } else {
                    cY = A.y;
                }

                //If the closest point is inside the circle
                if (distance(A.x, A.y, cX, cY) < A.r) {
                    //This box and the circle have collided
                    return true;
                }
            }

            //If the shapes have not collided
            return false;
        };
        
        namespace Graphic {
            class Texture;
            class Window;
            typedef std::map<std::string, Texture*> Textures;
            typedef Textures::iterator TexIter;
            typedef std::map<std::string, Window* > Windows;
            typedef Windows::iterator WindowIter;
            
            
            
        }
    }
}

#endif
/* UTILITY_H */

